//
//  ADPermissionManager.m
//  ADKit
//
//  Created by duzexu on 2017/12/21.
//

#import "ADPermissionManager.h"
#import "ADKitHeaders.h"
#import <AVFoundation/AVFoundation.h>
#if AD_PERMISSION_CONTACT
@import AddressBook;
#endif

@interface ADLocationHelper : NSObject<CLLocationManagerDelegate>
{
@public
    CLLocationManager *_manager;
    ADPermissonStatusBlock _locationBlock;
}
@end

@implementation  ADLocationHelper

- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
    if (!_locationBlock) return;
    if (status == kCLAuthorizationStatusNotDetermined) return;  // first request location
    
    if (status == kCLAuthorizationStatusAuthorizedAlways ||
        status == kCLAuthorizationStatusAuthorizedWhenInUse) {
        _locationBlock(ADAuthorityStatusAuthorized);
    }else if (status == kCLAuthorizationStatusDenied){
        _locationBlock(ADAuthorityStatusDenied);
    }
    _locationBlock = nil;
    _manager.delegate = nil;
}

@end

static ADLocationHelper *_helper;
static ADPermissionEntity *_entity;

@implementation ADPermissionManager

+ (void)openAppSetting {
    if (@available(iOS 8.0, *)) {
        NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
        if ([[UIApplication sharedApplication] canOpenURL:url]) {
            if (@available(iOS 10.0, *)) {
                [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil];
            }else {
                [[UIApplication sharedApplication] openURL:url];
            }
        }
    }
}

+ (ADPermissionEntity *)defaultPermissionEntity {
    if (!_entity) {
        _entity = [ADPermissionEntity new];
    }
    return _entity;
}

+ (ADAuthorityStatus)checkAuthorityWithType:(ADAuthorityType)type {
    switch (type) {
        case ADAuthorityTypeAlbum:
            return [self checkAlbumAuthority];
            break;
        case ADAuthorityTypeCamera:
            return [self checkCameraAuthority];
            break;
        case ADAuthorityTypeContact:
            return [self checkContactAuthority];
            break;
        case ADAuthorityTypeMic:
            return [self checkMicAuthority];
            break;
        case ADAuthorityTypeMedia:
            return [self checkMediaAuthority];
            break;
        case ADAuthorityTypeLocation:
            return [self checkLocationAuthority];
            break;
        case ADAuthorityTypeNotification:
            return [self checkNotiAuthority];
            break;
        case ADAuthorityTypeCalendar:
            return [self checkCalendarAuthority];
            break;
        case ADAuthorityTypeReminder:
            return [self checkReminderAuthority];
            break;
    }
    return 0;
}

+ (void)requestPermissionWithType:(ADAuthorityType)type status:(ADPermissonStatusBlock)statusBlock {
    if (type != ADAuthorityTypeNotification) {
        ADAuthorityStatus status = [self checkAuthorityWithType:type];
        if (status != ADAuthorityStatusNotDetermined) {
            if (statusBlock) {
                statusBlock(status);
            }
            return;
        }
    }
    switch (type) {
        case ADAuthorityTypeAlbum:
            [self requestAlbumPermission:statusBlock];
            break;
        case ADAuthorityTypeCamera:
            [self requestCameraPermission:statusBlock];
            break;
        case ADAuthorityTypeContact:
            [self requestContactPermission:statusBlock];
            break;
        case ADAuthorityTypeMic:
            [self requestMicPermission:statusBlock];
            break;
        case ADAuthorityTypeMedia:
            [self requestMediaPermission:statusBlock];
            break;
        case ADAuthorityTypeLocation:
            [self requestLocationWithType:[self defaultPermissionEntity].locationType Permission:statusBlock];
            break;
        case ADAuthorityTypeNotification:
            [self requestNotiPermission:statusBlock];
            break;
        case ADAuthorityTypeCalendar:
            [self requestCalendarPermission:statusBlock];
            break;
        case ADAuthorityTypeReminder:
            [self requestReminderPermission:statusBlock];
            break;
    }
}

#pragma mark - 相册
+ (ADAuthorityStatus)checkAlbumAuthority {
#if AD_PERMISSION_ALBUM
    if (NSClassFromString(@"PHPhotoLibrary")) {
        return (NSUInteger)[NSClassFromString(@"PHPhotoLibrary") performSelector:@selector(authorizationStatus)];
    }else{
        return (NSUInteger)[NSClassFromString(@"ALAssetsLibrary") performSelector:@selector(authorizationStatus)];
    }
#else
    NSAssert(NO, @"Please Set AD_PERMISSION_ALBUM to (1)");
    return 0;
#endif
}

+ (void)requestAlbumPermission:(ADPermissonStatusBlock)statusBlock {
#if AD_PERMISSION_ALBUM
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
    if (NSClassFromString(@"PHPhotoLibrary")) {
        [NSClassFromString(@"PHPhotoLibrary") performSelector:@selector(requestAuthorization:) withObject:statusBlock];
    }else{
        NSUInteger status = [NSClassFromString(@"ALAssetsLibrary") performSelector:@selector(authorizationStatus)];
        statusBlock(status);
    }
#pragma clang diagnostic pop
#else
    NSAssert(NO, @"Please Set AD_PERMISSION_ALBUM to (1)");
#endif
}

#pragma mark - 相机
+ (ADAuthorityStatus)checkCameraAuthority {
    return (NSUInteger)[NSClassFromString(@"AVCaptureDevice") performSelector:@selector(authorizationStatusForMediaType:) withObject:AVMediaTypeVideo];
}

+ (void)requestCameraPermission:(ADPermissonStatusBlock)statusBlock {
    AD_VoidBlock_bool block = ^(BOOL granted) {
        if (granted) {
            statusBlock(ADAuthorityStatusAuthorized);
        }else{
            statusBlock(ADAuthorityStatusDenied);
        }
    };
    [NSClassFromString(@"AVCaptureDevice") performSelectorWithArgsOnMainThread:@selector(requestAccessForMediaType:completionHandler:) waitUntilDone:NO, AVMediaTypeVideo,block];
}

#pragma mark - 麦克风
+ (ADAuthorityStatus)checkMicAuthority {
    return (NSUInteger)[NSClassFromString(@"AVCaptureDevice") performSelector:@selector(authorizationStatusForMediaType:) withObject:AVMediaTypeAudio];
}

+ (void)requestMicPermission:(ADPermissonStatusBlock)statusBlock {
    AD_VoidBlock_bool block = ^(BOOL granted) {
        if (granted) {
            statusBlock(ADAuthorityStatusAuthorized);
        }else{
            statusBlock(ADAuthorityStatusDenied);
        }
    };
    [NSClassFromString(@"AVCaptureDevice") performSelectorWithArgsOnMainThread:@selector(requestAccessForMediaType:completionHandler:) waitUntilDone:NO, AVMediaTypeAudio,block];
}

#pragma mark - 定位
+ (ADAuthorityStatus)checkLocationAuthority {
    BOOL flag = [NSClassFromString(@"CLLocationManager") performSelector:@selector(locationServicesEnabled)];
    if (!flag) {
        return ADAuthorityStatusOff;
    }
    CLAuthorizationStatus status = (CLAuthorizationStatus)[NSClassFromString(@"CLLocationManager") performSelector:@selector(authorizationStatus)];
    if (status == kCLAuthorizationStatusAuthorizedAlways ||
        status == kCLAuthorizationStatusAuthorizedWhenInUse) {
        return ADAuthorityStatusAuthorized;
    }
    return (NSUInteger)status;
}

+ (void)requestLocationWithType:(ADLocationType)type Permission:(ADPermissonStatusBlock)statusBlock {
    if (!_helper) {
        _helper = [[ADLocationHelper alloc] init];
    }
    CLLocationManager *manager = [[NSClassFromString(@"CLLocationManager") alloc] init];
    _helper->_locationBlock = statusBlock;
    _helper->_manager = manager;
    manager.delegate = _helper;
    if (type == ADLocationTypeWhenInUse) {
        [manager requestWhenInUseAuthorization];
    }else{
        [manager requestAlwaysAuthorization];
    }
}

#pragma mark - 推送
+ (ADAuthorityStatus)checkNotiAuthority {
    if (NSClassFromString(@"UNUserNotificationCenter")) {
        __block ADAuthorityStatus status = ADAuthorityStatusNotDetermined;
        dispatch_semaphore_t sem = dispatch_semaphore_create(0);
        AD_VoidBlock_id block = ^(id settings) {
            NSInteger authorizationStatus = (NSInteger)[settings performSelector:@selector(authorizationStatus)];
            if (authorizationStatus == 1) {
                status = ADAuthorityStatusDenied;
            }else if (authorizationStatus == 2){
                status = ADAuthorityStatusAuthorized;
            }else{
                status = ADAuthorityStatusNotDetermined;
            }
            dispatch_semaphore_signal(sem);
        };
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
        id notiCenter = [NSClassFromString(@"UNUserNotificationCenter") performSelector:@selector(currentNotificationCenter)];
        [notiCenter performSelector:@selector(getNotificationSettingsWithCompletionHandler:) withObject:block];
#pragma clang diagnostic pop
        dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
        return status;
    }else{
        UIUserNotificationSettings *settings = [[UIApplication sharedApplication] currentUserNotificationSettings];
        UIUserNotificationType notiTypes = settings.types;
        if (notiTypes == UIUserNotificationTypeNone) {
            return ADAuthorityStatusDenied;
        }else{
            return ADAuthorityStatusAuthorized;
        }
    }
}

+ (void)requestNotiPermission:(ADPermissonStatusBlock)statusBlock {
    [self defaultPermissionEntity];
    if (NSClassFromString(@"UNUserNotificationCenter")) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
        id notiCenter = [NSClassFromString(@"UNUserNotificationCenter") performSelector:@selector(currentNotificationCenter)];
        NSUInteger options = _entity.types;
        id block = ^(BOOL granted, NSError *error) {
            if (granted) {
                statusBlock(ADAuthorityStatusAuthorized);
            }else{
                statusBlock(ADAuthorityStatusDenied);
            }
        };
        if ([_entity.categories.anyObject isKindOfClass:NSClassFromString(@"UNNotificationCategory")]) {
            [notiCenter performSelector:@selector(setNotificationCategories:) withObject:_entity.categories];
        }
        [notiCenter performSelectorWithArgsOnMainThread:@selector(requestAuthorizationWithOptions:completionHandler:) waitUntilDone:NO,options,block];
#pragma clang diagnostic pop
        [[UIApplication sharedApplication] registerForRemoteNotifications];
    }else{
        if (@available(iOS 8.0, *)) {
            NSSet *categories = nil;
            if ([_entity.categories.anyObject isKindOfClass:[UIUserNotificationCategory class]]) {
                categories = _entity.categories;
            }
            UIUserNotificationSettings *setting = [UIUserNotificationSettings settingsForTypes:_entity.types categories:categories];
            [[UIApplication sharedApplication] registerUserNotificationSettings:setting];
            [[UIApplication sharedApplication] registerForRemoteNotifications];
        }else{
            [[UIApplication sharedApplication] registerForRemoteNotificationTypes:_entity.types];
        }
    }
}

#pragma mark - 通讯录
+ (ADAuthorityStatus)checkContactAuthority {
#if AD_PERMISSION_CONTACT
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
    if (NSClassFromString(@"CNContactStore")) {
        return (NSUInteger)[NSClassFromString(@"CNContactStore") performSelectorWithArgsOnMainThread:@selector(authorizationStatusForEntityType:) waitUntilDone:YES,0];
#pragma clang diagnostic pop
    }else{
        return (NSUInteger)ABAddressBookGetAuthorizationStatus();
    }
#else
    NSAssert(NO, @"Please Set AD_PERMISSION_CONTACT to (1)");
    return 0;
#endif
}

+ (void)requestContactPermission:(ADPermissonStatusBlock)statusBlock {
#if AD_PERMISSION_CONTACT
    if (NSClassFromString(@"CNContactStore")) {
        id contact = [[NSClassFromString(@"CNContactStore") alloc] init];
        id block = ^(BOOL granted, NSError *error) {
            if (granted) {
                statusBlock(ADAuthorityStatusAuthorized);
            }else{
                statusBlock(ADAuthorityStatusDenied);
            }
        };
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
        [contact performSelectorWithArgsOnMainThread:@selector(requestAccessForEntityType:completionHandler:) waitUntilDone:NO,0,block];
#pragma clang diagnostic pop
    }else{
        ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, NULL);
        ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {
            if (granted) {
                statusBlock(ADAuthorityStatusAuthorized);
            }else{
                statusBlock(ADAuthorityStatusDenied);
            }
            CFRelease(addressBook);
        });
    }
#else
    NSAssert(NO, @"Please Set AD_PERMISSION_CONTACT to (1)");
#endif
}

#pragma mark - 日历
+ (ADAuthorityStatus)checkCalendarAuthority {
#if AD_PERMISSION_CALENDAR
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
    return (NSUInteger)[NSClassFromString(@"EKEventStore") performSelectorWithArgsOnMainThread:@selector(authorizationStatusForEntityType:) waitUntilDone:YES,0];
#pragma clang diagnostic pop
#else
    NSAssert(NO, @"Please Set AD_PERMISSION_CALENDAR to (1)");
    return 0;
#endif
}

+ (void)requestCalendarPermission:(ADPermissonStatusBlock)statusBlock {
#if AD_PERMISSION_CALENDAR
    id event = [[NSClassFromString(@"EKEventStore") alloc] init];
    id block = ^(BOOL granted, NSError *error) {
        if (granted) {
            statusBlock(ADAuthorityStatusAuthorized);
        }else{
            statusBlock(ADAuthorityStatusDenied);
        }
    };
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
    [event performSelectorWithArgsOnMainThread:@selector(requestAccessToEntityType:completion:) waitUntilDone:NO,0,block];
#pragma clang diagnostic pop
#else
    NSAssert(NO, @"Please Set AD_PERMISSION_CALENDAR to (1)");
#endif
}

#pragma mark - 备忘录
+ (ADAuthorityStatus)checkReminderAuthority {
#if AD_PERMISSION_CALENDAR
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
    return (NSUInteger)[NSClassFromString(@"EKEventStore") performSelectorWithArgsOnMainThread:@selector(authorizationStatusForEntityType:) waitUntilDone:YES,1];
#pragma clang diagnostic pop
#else
    NSAssert(NO, @"Please Set AD_PERMISSION_CALENDAR to (1)");
    return 0;
#endif
}

+ (void)requestReminderPermission:(ADPermissonStatusBlock)statusBlock {
#if AD_PERMISSION_CALENDAR
    id event = [[NSClassFromString(@"EKEventStore") alloc] init];
    id block = ^(BOOL granted, NSError *error) {
        if (granted) {
            statusBlock(ADAuthorityStatusAuthorized);
        }else{
            statusBlock(ADAuthorityStatusDenied);
        }
    };
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
    [event performSelectorWithArgsOnMainThread:@selector(requestAccessToEntityType:completion:) waitUntilDone:NO,1,block];
#pragma clang diagnostic pop
#else
    NSAssert(NO, @"Please Set AD_PERMISSION_CALENDAR to (1)");
#endif
}

#pragma mark - 媒体
+ (ADAuthorityStatus)checkMediaAuthority {
#if AD_PERMISSION_MEDIA
    return (NSUInteger)[NSClassFromString(@"MPMediaLibrary") performSelector:@selector(authorizationStatus)];
#else
    NSAssert(NO, @"Please Set AD_PERMISSION_MEDIA to (1)");
    return 0;
#endif
}

+ (void)requestMediaPermission:(ADPermissonStatusBlock)statusBlock {
#if AD_PERMISSION_MEDIA
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
    [NSClassFromString(@"MPMediaLibrary") performSelector:@selector(requestAuthorization) withObject:statusBlock];
#pragma clang diagnostic pop
#else
    NSAssert(NO, @"Please Set AD_PERMISSION_MEDIA to (1)");
#endif
}

@end

@implementation ADPermissionEntity

- (instancetype)init {
    self = [super init];
    if (self) {
        _locationType = ADLocationTypeAlway;
        _types = UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge;
    }
    return self;
}

@end
